﻿// Decompiled with JetBrains decompiler
// Type: TaleWorlds.CampaignSystem.Party.MobileParty
// Assembly: TaleWorlds.CampaignSystem, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: E85F8C15-4DF6-4E9C-A58A-29177E40D07A
// Assembly location: D:\steam\steamapps\common\Mount & Blade II Bannerlord\bin\Win64_Shipping_Client\TaleWorlds.CampaignSystem.dll

using Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using TaleWorlds.CampaignSystem.Actions;
using TaleWorlds.CampaignSystem.CharacterDevelopment;
using TaleWorlds.CampaignSystem.ComponentInterfaces;
using TaleWorlds.CampaignSystem.GameState;
using TaleWorlds.CampaignSystem.Map;
using TaleWorlds.CampaignSystem.MapEvents;
using TaleWorlds.CampaignSystem.Party.PartyComponents;
using TaleWorlds.CampaignSystem.Roster;
using TaleWorlds.CampaignSystem.Settlements;
using TaleWorlds.CampaignSystem.Siege;
using TaleWorlds.Core;
using TaleWorlds.Library;
using TaleWorlds.Localization;
using TaleWorlds.ObjectSystem;
using TaleWorlds.SaveSystem;
using TaleWorlds.SaveSystem.Load;

#nullable disable
namespace TaleWorlds.CampaignSystem.Party
{
  public sealed class MobileParty : 
    CampaignObjectBase,
    ILocatable<MobileParty>,
    IMapPoint,
    ITrackableCampaignObject,
    ITrackableBase,
    IMapEntity,
    IRandomOwner
  {
    public const int DefaultPartyTradeInitialGold = 5000;
    public const int ClanRoleAssignmentMinimumSkillValue = 0;
    [SaveableField(1001)]
    private Settlement _currentSettlement;
    [CachedData]
    private MBList<MobileParty> _attachedParties;
    [SaveableField(1046)]
    private MobileParty _attachedTo;
    [SaveableField(1006)]
    public float HasUnpaidWages;
    [SaveableField(1060)]
    private Vec2 _eventPositionAdder;
    [SaveableField(1100)]
    private Vec2 _position2D;
    [SaveableField(1024)]
    private bool _isVisible;
    [CachedData]
    internal float _lastCalculatedSpeed = 1f;
    [SaveableField(1025)]
    private bool _isInspected;
    [SaveableField(1955)]
    private CampaignTime _disorganizedUntilTime;
    [CachedData]
    private int _partyPureSpeedLastCheckVersion = -1;
    [CachedData]
    private bool _partyLastCheckIsPrisoner;
    [CachedData]
    private ExplainedNumber _lastCalculatedBaseSpeedExplained;
    [CachedData]
    private bool _partyLastCheckAtNight;
    [CachedData]
    private int _itemRosterVersionNo = -1;
    [CachedData]
    private int _partySizeRatioLastCheckVersion = -1;
    [CachedData]
    private int _latestUsedPaymentRatio = -1;
    [CachedData]
    private float _cachedPartySizeRatio = 1f;
    [CachedData]
    private int _cachedPartySizeLimit;
    [SaveableField(1059)]
    private BesiegerCamp _besiegerCamp;
    [SaveableField(1048)]
    private MobileParty _targetParty;
    [SaveableField(1049)]
    private Settlement _targetSettlement;
    [SaveableField(1053)]
    private Vec2 _targetPosition;
    private int _doNotAttackMainParty;
    [SaveableField(1034)]
    private Settlement _customHomeSettlement;
    [SaveableField(1035)]
    private Army _army;
    [CachedData]
    private bool _isDisorganized;
    [SaveableField(1959)]
    private bool _isCurrentlyUsedByAQuest;
    [SaveableField(1956)]
    private int _partyTradeGold;
    [SaveableField(1063)]
    private CampaignTime _ignoredUntilTime;
    [SaveableField(1071)]
    public Vec2 AverageFleeTargetDirection;
    private bool _besiegerCampResetStarted;
    [CachedData]
    private int _locatorNodeIndex;
    [SaveableField(1120)]
    private Clan _actualClan;
    [SaveableField(1200)]
    private float _moraleDueToEvents;
    [CachedData]
    private PathFaceRecord _lastNavigationFace;
    [CachedData]
    private MapWeatherModel.WeatherEventEffectOnTerrain _lastWeatherTerrainEffect;
    [CachedData]
    private PathFaceRecord _currentNavigationFace;
    [SaveableField(210)]
    private PartyComponent _partyComponent;

    internal static void AutoGeneratedStaticCollectObjectsMobileParty(
      object o,
      List<object> collectedObjects)
    {
      ((MBObjectBase) o).AutoGeneratedInstanceCollectObjects(collectedObjects);
    }

    protected override void AutoGeneratedInstanceCollectObjects(List<object> collectedObjects)
    {
      base.AutoGeneratedInstanceCollectObjects(collectedObjects);
      collectedObjects.Add((object) this._currentSettlement);
      collectedObjects.Add((object) this._attachedTo);
      CampaignTime.AutoGeneratedStaticCollectObjectsCampaignTime((object) this._disorganizedUntilTime, collectedObjects);
      collectedObjects.Add((object) this._besiegerCamp);
      collectedObjects.Add((object) this._targetParty);
      collectedObjects.Add((object) this._targetSettlement);
      collectedObjects.Add((object) this._customHomeSettlement);
      collectedObjects.Add((object) this._army);
      CampaignTime.AutoGeneratedStaticCollectObjectsCampaignTime((object) this._ignoredUntilTime, collectedObjects);
      collectedObjects.Add((object) this._actualClan);
      collectedObjects.Add((object) this._partyComponent);
      collectedObjects.Add((object) this.CustomName);
      collectedObjects.Add((object) this.LastVisitedSettlement);
      collectedObjects.Add((object) this.Ai);
      collectedObjects.Add((object) this.Party);
      CampaignTime.AutoGeneratedStaticCollectObjectsCampaignTime((object) this.StationaryStartTime, collectedObjects);
      collectedObjects.Add((object) this.Scout);
      collectedObjects.Add((object) this.Engineer);
      collectedObjects.Add((object) this.Quartermaster);
      collectedObjects.Add((object) this.Surgeon);
    }

    internal static object AutoGeneratedGetMemberValueCustomName(object o)
    {
      return (object) ((MobileParty) o).CustomName;
    }

    internal static object AutoGeneratedGetMemberValueLastVisitedSettlement(object o)
    {
      return (object) ((MobileParty) o).LastVisitedSettlement;
    }

    internal static object AutoGeneratedGetMemberValueBearing(object o)
    {
      return (object) ((MobileParty) o).Bearing;
    }

    internal static object AutoGeneratedGetMemberValueAggressiveness(object o)
    {
      return (object) ((MobileParty) o).Aggressiveness;
    }

    internal static object AutoGeneratedGetMemberValueArmyPositionAdder(object o)
    {
      return (object) ((MobileParty) o).ArmyPositionAdder;
    }

    internal static object AutoGeneratedGetMemberValueObjective(object o)
    {
      return (object) ((MobileParty) o).Objective;
    }

    internal static object AutoGeneratedGetMemberValueAi(object o) => (object) ((MobileParty) o).Ai;

    internal static object AutoGeneratedGetMemberValueParty(object o)
    {
      return (object) ((MobileParty) o).Party;
    }

    internal static object AutoGeneratedGetMemberValueIsActive(object o)
    {
      return (object) ((MobileParty) o).IsActive;
    }

    internal static object AutoGeneratedGetMemberValueShortTermBehavior(object o)
    {
      return (object) ((MobileParty) o).ShortTermBehavior;
    }

    internal static object AutoGeneratedGetMemberValueIsPartyTradeActive(object o)
    {
      return (object) ((MobileParty) o).IsPartyTradeActive;
    }

    internal static object AutoGeneratedGetMemberValuePartyTradeTaxGold(object o)
    {
      return (object) ((MobileParty) o).PartyTradeTaxGold;
    }

    internal static object AutoGeneratedGetMemberValueStationaryStartTime(object o)
    {
      return (object) ((MobileParty) o).StationaryStartTime;
    }

    internal static object AutoGeneratedGetMemberValueShouldJoinPlayerBattles(object o)
    {
      return (object) ((MobileParty) o).ShouldJoinPlayerBattles;
    }

    internal static object AutoGeneratedGetMemberValueIsDisbanding(object o)
    {
      return (object) ((MobileParty) o).IsDisbanding;
    }

    internal static object AutoGeneratedGetMemberValueScout(object o)
    {
      return (object) ((MobileParty) o).Scout;
    }

    internal static object AutoGeneratedGetMemberValueEngineer(object o)
    {
      return (object) ((MobileParty) o).Engineer;
    }

    internal static object AutoGeneratedGetMemberValueQuartermaster(object o)
    {
      return (object) ((MobileParty) o).Quartermaster;
    }

    internal static object AutoGeneratedGetMemberValueSurgeon(object o)
    {
      return (object) ((MobileParty) o).Surgeon;
    }

    internal static object AutoGeneratedGetMemberValueHasUnpaidWages(object o)
    {
      return (object) ((MobileParty) o).HasUnpaidWages;
    }

    internal static object AutoGeneratedGetMemberValueAverageFleeTargetDirection(object o)
    {
      return (object) ((MobileParty) o).AverageFleeTargetDirection;
    }

    internal static object AutoGeneratedGetMemberValue_currentSettlement(object o)
    {
      return (object) ((MobileParty) o)._currentSettlement;
    }

    internal static object AutoGeneratedGetMemberValue_attachedTo(object o)
    {
      return (object) ((MobileParty) o)._attachedTo;
    }

    internal static object AutoGeneratedGetMemberValue_eventPositionAdder(object o)
    {
      return (object) ((MobileParty) o)._eventPositionAdder;
    }

    internal static object AutoGeneratedGetMemberValue_position2D(object o)
    {
      return (object) ((MobileParty) o)._position2D;
    }

    internal static object AutoGeneratedGetMemberValue_isVisible(object o)
    {
      return (object) ((MobileParty) o)._isVisible;
    }

    internal static object AutoGeneratedGetMemberValue_isInspected(object o)
    {
      return (object) ((MobileParty) o)._isInspected;
    }

    internal static object AutoGeneratedGetMemberValue_disorganizedUntilTime(object o)
    {
      return (object) ((MobileParty) o)._disorganizedUntilTime;
    }

    internal static object AutoGeneratedGetMemberValue_besiegerCamp(object o)
    {
      return (object) ((MobileParty) o)._besiegerCamp;
    }

    internal static object AutoGeneratedGetMemberValue_targetParty(object o)
    {
      return (object) ((MobileParty) o)._targetParty;
    }

    internal static object AutoGeneratedGetMemberValue_targetSettlement(object o)
    {
      return (object) ((MobileParty) o)._targetSettlement;
    }

    internal static object AutoGeneratedGetMemberValue_targetPosition(object o)
    {
      return (object) ((MobileParty) o)._targetPosition;
    }

    internal static object AutoGeneratedGetMemberValue_customHomeSettlement(object o)
    {
      return (object) ((MobileParty) o)._customHomeSettlement;
    }

    internal static object AutoGeneratedGetMemberValue_army(object o)
    {
      return (object) ((MobileParty) o)._army;
    }

    internal static object AutoGeneratedGetMemberValue_isCurrentlyUsedByAQuest(object o)
    {
      return (object) ((MobileParty) o)._isCurrentlyUsedByAQuest;
    }

    internal static object AutoGeneratedGetMemberValue_partyTradeGold(object o)
    {
      return (object) ((MobileParty) o)._partyTradeGold;
    }

    internal static object AutoGeneratedGetMemberValue_ignoredUntilTime(object o)
    {
      return (object) ((MobileParty) o)._ignoredUntilTime;
    }

    internal static object AutoGeneratedGetMemberValue_actualClan(object o)
    {
      return (object) ((MobileParty) o)._actualClan;
    }

    internal static object AutoGeneratedGetMemberValue_moraleDueToEvents(object o)
    {
      return (object) ((MobileParty) o)._moraleDueToEvents;
    }

    internal static object AutoGeneratedGetMemberValue_partyComponent(object o)
    {
      return (object) ((MobileParty) o)._partyComponent;
    }

    public static MobileParty MainParty => Campaign.Current.MainParty;

    public static MBReadOnlyList<MobileParty> All => Campaign.Current.MobileParties;

    public static MBReadOnlyList<MobileParty> AllCaravanParties => Campaign.Current.CaravanParties;

    public static MBReadOnlyList<MobileParty> AllBanditParties => Campaign.Current.BanditParties;

    public static MBReadOnlyList<MobileParty> AllLordParties => Campaign.Current.LordParties;

    public static MBReadOnlyList<MobileParty> AllGarrisonParties
    {
      get => Campaign.Current.GarrisonParties;
    }

    public static MBReadOnlyList<MobileParty> AllMilitiaParties => Campaign.Current.MilitiaParties;

    public static MBReadOnlyList<MobileParty> AllVillagerParties
    {
      get => Campaign.Current.VillagerParties;
    }

    public static MBReadOnlyList<MobileParty> AllCustomParties => Campaign.Current.CustomParties;

    public static MBReadOnlyList<MobileParty> AllPartiesWithoutPartyComponent
    {
      get => Campaign.Current.PartiesWithoutPartyComponent;
    }

    public static int Count => Campaign.Current.MobileParties.Count;

    public static MobileParty ConversationParty
    {
      get => Campaign.Current.ConversationManager.ConversationParty;
    }

    [SaveableProperty(1021)]
    private TextObject CustomName { get; set; }

    public TextObject Name
    {
      get
      {
        if (!TextObject.IsNullOrEmpty(this.CustomName))
          return this.CustomName;
        return this._partyComponent == null ? new TextObject("{=!}unnamedMobileParty") : this._partyComponent.Name;
      }
    }

    [SaveableProperty(1002)]
    public Settlement LastVisitedSettlement { get; private set; }

    [SaveableProperty(1004)]
    public Vec2 Bearing { get; internal set; }

    public MBReadOnlyList<MobileParty> AttachedParties
    {
      get => (MBReadOnlyList<MobileParty>) this._attachedParties;
    }

    [SaveableProperty(1009)]
    public float Aggressiveness { get; set; }

    public int PaymentLimit
    {
      get
      {
        PartyComponent partyComponent = this._partyComponent;
        return partyComponent == null ? Campaign.Current.Models.PartyWageModel.MaxWage : partyComponent.WagePaymentLimit;
      }
    }

    public bool HasLimitedWage()
    {
      return this.PaymentLimit != Campaign.Current.Models.PartyWageModel.MaxWage;
    }

    public bool CanPayMoreWage() => !this.HasLimitedWage() || this.PaymentLimit > this.TotalWage;

    public bool IsWageLimitExceeded()
    {
      return this.HasLimitedWage() && this.PaymentLimit < this.TotalWage;
    }

    public void SetWagePaymentLimit(int newLimit)
    {
      this.PartyComponent?.SetWagePaymentLimit(newLimit);
    }

    [SaveableProperty(1005)]
    public Vec2 ArmyPositionAdder { get; private set; }

    public Vec2 AiBehaviorTarget => this.Ai.BehaviorTarget;

    [SaveableProperty(1090)]
    public MobileParty.PartyObjective Objective { get; private set; }

    [CachedData]
    MobileParty ILocatable<MobileParty>.NextLocatable { get; set; }

    [SaveableProperty(1019)]
    public MobilePartyAi Ai { get; private set; }

    [SaveableProperty(1020)]
    public PartyBase Party { get; private set; }

    [SaveableProperty(1023)]
    public bool IsActive { get; set; }

    public CampaignTime DisorganizedUntilTime => this._disorganizedUntilTime;

    [CachedData]
    public PartyThinkParams ThinkParamsCache { get; private set; }

    public float Speed => this.CalculateSpeed();

    public ExplainedNumber SpeedExplained
    {
      get
      {
        this._lastCalculatedBaseSpeedExplained = Campaign.Current.Models.PartySpeedCalculatingModel.CalculateBaseSpeed(this, true);
        ExplainedNumber finalSpeed = Campaign.Current.Models.PartySpeedCalculatingModel.CalculateFinalSpeed(this, this._lastCalculatedBaseSpeedExplained);
        this._lastCalculatedSpeed = finalSpeed.ResultNumber;
        return finalSpeed;
      }
    }

    public MobileParty ShortTermTargetParty => this.Ai.AiBehaviorPartyBase?.MobileParty;

    public Settlement ShortTermTargetSettlement => this.Ai.AiBehaviorPartyBase?.Settlement;

    public bool IsDisorganized => this._isDisorganized;

    public bool IsCurrentlyUsedByAQuest => this._isCurrentlyUsedByAQuest;

    [SaveableProperty(1050)]
    public AiBehavior ShortTermBehavior { get; internal set; }

    [SaveableProperty(1958)]
    public bool IsPartyTradeActive { get; private set; }

    public int PartyTradeGold
    {
      get => this._partyTradeGold;
      set => this._partyTradeGold = MathF.Max(value, 0);
    }

    [SaveableProperty(1957)]
    public int PartyTradeTaxGold { get; private set; }

    [SaveableProperty(1960)]
    public CampaignTime StationaryStartTime { get; private set; }

    [CachedData]
    public int VersionNo { get; private set; }

    [SaveableProperty(1080)]
    public bool ShouldJoinPlayerBattles { get; set; }

    [SaveableProperty(1081)]
    public bool IsDisbanding { get; set; }

    public int RandomValue => this.Party.RandomValue;

    public Settlement CurrentSettlement
    {
      get => this._currentSettlement;
      set
      {
        if (value == this._currentSettlement)
          return;
        if (this._currentSettlement != null)
          this._currentSettlement.RemoveMobileParty(this);
        this._currentSettlement = value;
        if (this._currentSettlement != null)
        {
          this._currentSettlement.AddMobileParty(this);
          if (this._currentSettlement.IsFortification)
            this.Position2D = this._currentSettlement.GatePosition;
          this.LastVisitedSettlement = value;
        }
        foreach (MobileParty attachedParty in (List<MobileParty>) this._attachedParties)
          attachedParty.CurrentSettlement = value;
        if (this._currentSettlement != null && this._currentSettlement.IsFortification)
        {
          this.ArmyPositionAdder = Vec2.Zero;
          this.ErrorPosition = Vec2.Zero;
          this.Bearing = Vec2.Zero;
          this.Party.AverageBearingRotation = 0.0f;
          foreach (MobileParty party in (List<MobileParty>) this._currentSettlement.Parties)
            party.Party.SetVisualAsDirty();
        }
        this.Party.SetVisualAsDirty();
      }
    }

    public Settlement HomeSettlement
    {
      get
      {
        Settlement customHomeSettlement = this._customHomeSettlement;
        if (customHomeSettlement != null)
          return customHomeSettlement;
        return this._partyComponent?.HomeSettlement;
      }
    }

    public void SetCustomHomeSettlement(Settlement customHomeSettlement)
    {
      this._customHomeSettlement = customHomeSettlement;
    }

    public MobileParty AttachedTo
    {
      get => this._attachedTo;
      set
      {
        if (this._attachedTo == value)
          return;
        this.SetAttachedToInternal(value);
      }
    }

    private void SetAttachedToInternal(MobileParty value)
    {
      if (this._attachedTo != null)
      {
        this._attachedTo.RemoveAttachedPartyInternal(this);
        if (this.Party.MapEventSide != null)
        {
          this.Party.MapEventSide.HandleMapEventEndForPartyInternal(this.Party);
          this.Party.MapEventSide = (MapEventSide) null;
        }
        if (this.BesiegerCamp != null)
          this.BesiegerCamp = (BesiegerCamp) null;
        this.OnAttachedToRemoved();
      }
      this._attachedTo = value;
      if (this._attachedTo != null)
      {
        this._attachedTo.AddAttachedPartyInternal(this);
        this.Party.MapEventSide = this._attachedTo.Party.MapEventSide;
        this.BesiegerCamp = this._attachedTo.BesiegerCamp;
        this.CurrentSettlement = this._attachedTo.CurrentSettlement;
      }
      this.Party.SetVisualAsDirty();
    }

    private void AddAttachedPartyInternal(MobileParty mobileParty)
    {
      if (this._attachedParties == null)
        this._attachedParties = new MBList<MobileParty>();
      this._attachedParties.Add(mobileParty);
      if (CampaignEventDispatcher.Instance == null)
        return;
      CampaignEventDispatcher.Instance.OnPartyAttachedAnotherParty(mobileParty);
    }

    private void RemoveAttachedPartyInternal(MobileParty mobileParty)
    {
      this._attachedParties.Remove(mobileParty);
    }

    private void OnAttachedToRemoved()
    {
      this.ErrorPosition += this.ArmyPositionAdder;
      this.ArmyPositionAdder = Vec2.Zero;
      if (!this.IsVisible)
        this.ErrorPosition = Vec2.Zero;
      if (this.CurrentSettlement != null)
        this.Ai.SetMoveGoToSettlement(this.CurrentSettlement);
      else
        this.Ai.SetMoveModeHold();
    }

    public Army Army
    {
      get => this._army;
      set
      {
        if (this._army == value)
          return;
        this.UpdateVersionNo();
        if (this._army != null)
          this._army.OnRemovePartyInternal(this);
        this._army = value;
        if (value == null)
        {
          if (this != MobileParty.MainParty || !(Game.Current.GameStateManager.ActiveState is MapState))
            return;
          ((MapState) Game.Current.GameStateManager.ActiveState).OnLeaveArmy();
        }
        else
        {
          this._army.OnAddPartyInternal(this);
          this.Ai.ResetNumberOfRecentFleeing();
        }
      }
    }

    public BesiegerCamp BesiegerCamp
    {
      get => this._besiegerCamp;
      set
      {
        if (this._besiegerCamp == value || this._besiegerCampResetStarted)
          return;
        if (this._besiegerCamp != null)
          this.OnPartyLeftSiegeInternal();
        this._besiegerCamp = value;
        if (this._besiegerCamp != null)
          this.OnPartyJoinedSiegeInternal();
        foreach (MobileParty attachedParty in (List<MobileParty>) this._attachedParties)
          attachedParty.BesiegerCamp = value;
        this.Party.SetVisualAsDirty();
      }
    }

    public AiBehavior DefaultBehavior => this.Ai.DefaultBehavior;

    public Settlement TargetSettlement
    {
      get => this._targetSettlement;
      internal set
      {
        if (value == this._targetSettlement)
          return;
        this._targetSettlement = value;
        this.Ai.DefaultBehaviorNeedsUpdate = true;
      }
    }

    public Vec2 TargetPosition
    {
      get => this._targetPosition;
      internal set
      {
        if (!(this._targetPosition != value))
          return;
        this._targetPosition = value;
        this.Ai.DefaultBehaviorNeedsUpdate = true;
      }
    }

    public MobileParty TargetParty
    {
      get => this._targetParty;
      internal set
      {
        if (value == this._targetParty)
          return;
        this._targetParty = value;
        this.Ai.DefaultBehaviorNeedsUpdate = true;
      }
    }

    public MobileParty()
    {
      this._isVisible = false;
      this.IsActive = true;
      this._isCurrentlyUsedByAQuest = false;
      this.Party = new PartyBase(this);
      this.InitMembers();
      this.InitCached();
      this.Initialize();
    }

    private void InitMembers()
    {
      if (this._attachedParties != null)
        return;
      this._attachedParties = new MBList<MobileParty>();
    }

    public Hero LeaderHero => this.PartyComponent?.Leader;

    [SaveableProperty(1070)]
    private Hero Scout { get; set; }

    [SaveableProperty(1072)]
    private Hero Engineer { get; set; }

    [SaveableProperty(1071)]
    private Hero Quartermaster { get; set; }

    [SaveableProperty(1073)]
    private Hero Surgeon { get; set; }

    public Hero Owner => this._partyComponent?.PartyOwner;

    public Hero EffectiveScout
    {
      get
      {
        return this.Scout == null || this.Scout.PartyBelongedTo != this ? this.LeaderHero : this.Scout;
      }
    }

    public Hero EffectiveQuartermaster
    {
      get
      {
        return this.Quartermaster == null || this.Quartermaster.PartyBelongedTo != this ? this.LeaderHero : this.Quartermaster;
      }
    }

    public Hero EffectiveEngineer
    {
      get
      {
        return this.Engineer == null || this.Engineer.PartyBelongedTo != this ? this.LeaderHero : this.Engineer;
      }
    }

    public Hero EffectiveSurgeon
    {
      get
      {
        return this.Surgeon == null || this.Surgeon.PartyBelongedTo != this ? this.LeaderHero : this.Surgeon;
      }
    }

    public void SetPartyScout(Hero hero)
    {
      this.RemoveHeroPerkRole(hero);
      this.Scout = hero;
    }

    public void SetPartyQuartermaster(Hero hero)
    {
      this.RemoveHeroPerkRole(hero);
      this.Quartermaster = hero;
    }

    public void SetPartyEngineer(Hero hero)
    {
      this.RemoveHeroPerkRole(hero);
      this.Engineer = hero;
    }

    public void SetPartySurgeon(Hero hero)
    {
      this.RemoveHeroPerkRole(hero);
      this.Surgeon = hero;
    }

    private void InitializeMobilePartyWithPartyTemplate(
      PartyTemplateObject pt,
      Vec2 position,
      int troopNumberLimit)
    {
      if (troopNumberLimit != 0)
        this.FillPartyStacks(pt, troopNumberLimit);
      this.CreateFigure(position, 0.0f);
      this.Ai.SetMoveModeHold();
    }

    public void InitializeMobilePartyAroundPosition(
      TroopRoster memberRoster,
      TroopRoster prisonerRoster,
      Vec2 position,
      float spawnRadius,
      float minSpawnRadius = 0.0f)
    {
      position = MobilePartyHelper.FindReachablePointAroundPosition(position, spawnRadius, minSpawnRadius);
      this.InitializeMobilePartyWithRosterInternal(memberRoster, prisonerRoster, position);
    }

    public override void Initialize()
    {
      base.Initialize();
      this.Aggressiveness = 1f;
      this.Ai = new MobilePartyAi(this);
      CampaignEventDispatcher.Instance.OnPartyVisibilityChanged(this.Party);
    }

    public void InitializeMobilePartyAtPosition(
      TroopRoster memberRoster,
      TroopRoster prisonerRoster,
      Vec2 position)
    {
      this.InitializeMobilePartyWithRosterInternal(memberRoster, prisonerRoster, position);
    }

    public void InitializeMobilePartyAtPosition(
      PartyTemplateObject pt,
      Vec2 position,
      int troopNumberLimit = -1)
    {
      this.InitializeMobilePartyWithPartyTemplate(pt, position, troopNumberLimit);
    }

    public void InitializeMobilePartyAroundPosition(
      PartyTemplateObject pt,
      Vec2 position,
      float spawnRadius,
      float minSpawnRadius = 0.0f,
      int troopNumberLimit = -1)
    {
      position = MobilePartyHelper.FindReachablePointAroundPosition(position, spawnRadius, minSpawnRadius);
      this.InitializeMobilePartyWithPartyTemplate(pt, position, troopNumberLimit);
    }

    private void InitializeMobilePartyWithRosterInternal(
      TroopRoster memberRoster,
      TroopRoster prisonerRoster,
      Vec2 position)
    {
      this.MemberRoster.Add(memberRoster);
      this.PrisonRoster.Add(prisonerRoster);
      this.CreateFigure(position, 0.0f);
      this.Ai.SetMoveModeHold();
    }

    public Clan ActualClan
    {
      get => this._actualClan;
      set
      {
        if (this._actualClan == value)
          return;
        if (this._actualClan != null && value != null && this.PartyComponent is WarPartyComponent partyComponent)
          partyComponent.OnClanChange(this._actualClan, value);
        this._actualClan = value;
      }
    }

    internal void StartUp() => this.Ai.StartUp();

    [LateLoadInitializationCallback]
    private void OnLateLoad(MetaData metaData, ObjectLoadData objectLoadData)
    {
      if (!(MBSaveLoad.LastLoadedGameVersion < ApplicationVersion.FromString("v1.1.0")))
        return;
      PartyBase memberValueBySaveId1 = (PartyBase) objectLoadData.GetMemberValueBySaveId(1052);
      IMapEntity mapEntity = (IMapEntity) null;
      if (memberValueBySaveId1 != null)
      {
        if (memberValueBySaveId1.IsSettlement)
          mapEntity = (IMapEntity) memberValueBySaveId1.Settlement;
        else if (memberValueBySaveId1.IsMobile)
          mapEntity = (IMapEntity) memberValueBySaveId1.MobileParty;
      }
      object memberValueBySaveId2 = objectLoadData.GetMemberValueBySaveId(1036);
      object memberValueBySaveId3 = objectLoadData.GetMemberValueBySaveId(1037);
      object memberValueBySaveId4 = objectLoadData.GetMemberValueBySaveId(1064);
      object memberValueBySaveId5 = objectLoadData.GetMemberValueBySaveId(1065);
      object memberValueBySaveId6 = objectLoadData.GetMemberValueBySaveId(1047);
      object memberValueBySaveId7 = objectLoadData.GetMemberValueBySaveId(1051);
      object memberValueBySaveId8 = objectLoadData.GetMemberValueBySaveId(1038);
      object memberValueBySaveId9 = objectLoadData.GetMemberValueBySaveId(1039);
      object memberValueBySaveId10 = objectLoadData.GetMemberValueBySaveId(1055);
      object memberValueBySaveId11 = objectLoadData.GetMemberValueBySaveId(1054);
      object memberValueBySaveId12 = objectLoadData.GetMemberValueBySaveId(1062);
      object memberValueBySaveId13 = objectLoadData.GetMemberValueBySaveId(1061);
      object fieldValueBySaveId = objectLoadData.GetFieldValueBySaveId(1070);
      object memberValueBySaveId14 = objectLoadData.GetMemberValueBySaveId(1022);
      object oldAiBehaviorMapEntity = (object) mapEntity ?? objectLoadData.GetMemberValueBySaveId(1056);
      object memberValueBySaveId15 = objectLoadData.GetMemberValueBySaveId(1074);
      if (memberValueBySaveId2 != null)
        this.Ai.InitializeForOldSaves((float) memberValueBySaveId2, (float) memberValueBySaveId3, (CampaignTime) memberValueBySaveId4, (int) memberValueBySaveId5, (AiBehavior) memberValueBySaveId6, (Vec2) memberValueBySaveId7, (bool) memberValueBySaveId8, (bool) memberValueBySaveId9, (MoveModeType) memberValueBySaveId10, (MobileParty) memberValueBySaveId11, (Vec2) memberValueBySaveId12, (Vec2) memberValueBySaveId13, (Vec2) fieldValueBySaveId, (Vec2) memberValueBySaveId14, (IMapEntity) oldAiBehaviorMapEntity, (CampaignTime?) memberValueBySaveId15 ?? CampaignTime.Never);
      this.UpdatePartyComponentFlags();
      if (!this.IsGarrison && !this.IsLordParty)
        return;
      object memberValueBySaveId16 = objectLoadData.GetMemberValueBySaveId(1010);
      if (memberValueBySaveId16 == null)
        return;
      this.SetWagePaymentLimit((int) memberValueBySaveId16);
    }

    public float RecentEventsMorale
    {
      get => this._moraleDueToEvents;
      set
      {
        this._moraleDueToEvents = value;
        if ((double) this._moraleDueToEvents < -50.0)
        {
          this._moraleDueToEvents = -50f;
        }
        else
        {
          if ((double) this._moraleDueToEvents <= 50.0)
            return;
          this._moraleDueToEvents = 50f;
        }
      }
    }

    public override string ToString() => this.StringId + ":" + (object) this.Party.Index;

    public float Morale
    {
      get
      {
        float resultNumber = Campaign.Current.Models.PartyMoraleModel.GetEffectivePartyMorale(this).ResultNumber;
        return (double) resultNumber < 0.0 ? 0.0f : ((double) resultNumber > 100.0 ? 100f : resultNumber);
      }
    }

    TextObject ITrackableBase.GetName() => this.Name;

    public ExplainedNumber MoraleExplained
    {
      get => Campaign.Current.Models.PartyMoraleModel.GetEffectivePartyMorale(this, true);
    }

    public void ValidateSpeed()
    {
      double speed = (double) this.CalculateSpeed();
    }

    public void ChangePartyLeader(Hero newLeader)
    {
      if (newLeader == null || !this.MemberRoster.Contains(newLeader.CharacterObject))
      {
        Debug.FailedAssert(newLeader?.Name.ToString() + " is not a member of " + (object) this.Name + "!\nParty leader did not change.", "C:\\Develop\\MB3\\Source\\Bannerlord\\TaleWorlds.CampaignSystem\\Party\\MobileParty.cs", nameof (ChangePartyLeader), 845);
      }
      else
      {
        if (this.IsLordParty && this._partyComponent is LordPartyComponent partyComponent)
          partyComponent.ChangePartyOwner(newLeader);
        this.PartyComponent.ChangePartyLeader(newLeader);
      }
    }

    public void RemovePartyLeader()
    {
      if (this.LeaderHero == null)
        return;
      if (this.MapEvent == null)
        this.Ai.SetMoveModeHold();
      this.PartyComponent.ChangePartyLeader((Hero) null);
    }

    private void RecoverPositionsForNavMeshUpdate()
    {
      if (this.Position2D.IsNonZero() && !PartyBase.IsPositionOkForTraveling(this.Position2D))
      {
        Debug.Print("Position of " + this.StringId + " is not valid! (" + (object) this.Position2D.x + ", " + (object) this.Position2D.y + ") Party will be moved to a valid position.");
        Settlement currentSettlement = this.CurrentSettlement;
        this.Position2D = currentSettlement != null ? currentSettlement.GatePosition : SettlementHelper.FindNearestVillage(toMapPoint: (IMapPoint) this).GatePosition;
      }
      if (this.CurrentSettlement != null && !this.CurrentSettlement.GatePosition.NearlyEquals(this.Position2D, this.CurrentSettlement.IsFortification ? Campaign.Current.Models.EncounterModel.NeededMaximumDistanceForEncounteringTown : Campaign.Current.Models.EncounterModel.NeededMaximumDistanceForEncounteringVillage))
      {
        Debug.Print("Position of " + this.StringId + " is not valid! (" + (object) this.Position2D.x + ", " + (object) this.Position2D.y + ") Party will be moved to a valid position.");
        this.Position2D = this.CurrentSettlement.GatePosition;
      }
      this.Ai.RecoverPositionsForNavMeshUpdate();
    }

    public void OnGameInitialized()
    {
      this.RecoverPositionsForNavMeshUpdate();
      Campaign current = Campaign.Current;
      if (current.MapSceneWrapper != null)
        this.CurrentNavigationFace = current.MapSceneWrapper.GetFaceIndex(this.Position2D);
      this.Ai.OnGameInitialized();
      if (this == Campaign.Current.CampaignObjectManager.Find<MobileParty>((Predicate<MobileParty>) (x => x.StringId == this.StringId)))
        return;
      DestroyPartyAction.Apply((PartyBase) null, this);
    }

    [LoadInitializationCallback]
    private void OnLoad(MetaData metaData, ObjectLoadData objectLoadData)
    {
      object memberValueBySaveId = objectLoadData.GetMemberValueBySaveId(1032);
      if (memberValueBySaveId == null)
        return;
      this._doNotAttackMainParty = (int) memberValueBySaveId;
    }

    protected override void PreAfterLoad()
    {
      this.UpdatePartyComponentFlags();
      this.PartyComponent?.Initialize(this);
      this.Ai.PreAfterLoad();
      if (this._disorganizedUntilTime.IsFuture)
        this._isDisorganized = true;
      if (!MBSaveLoad.IsUpdatingGameVersion || !(MBSaveLoad.LastLoadedGameVersion < ApplicationVersion.FromString("v1.2.2")))
        return;
      if (this.LeaderHero != null && this != MobileParty.MainParty && this.LeaderHero.PartyBelongedTo != this)
        DestroyPartyAction.Apply((PartyBase) null, this);
      if (this.MapEvent != null || !this.StringId.Contains("troops_of_CharacterObject") && !this.StringId.Contains("troops_of_TaleWorlds.CampaignSystem.CharacterObject"))
        return;
      DestroyPartyAction.Apply((PartyBase) null, this);
    }

    protected override void OnBeforeLoad()
    {
      this.Ai.OnBeforeLoad();
      this.InitMembers();
      this.InitCached();
      this._attachedTo?.AddAttachedPartyInternal(this);
    }

    private void InitCached()
    {
      this.Ai?.InitCached();
      ((ILocatable<MobileParty>) this).LocatorNodeIndex = -1;
      this.ThinkParamsCache = new PartyThinkParams(this);
      this.ResetCached();
    }

    private void ResetCached()
    {
      this._partySizeRatioLastCheckVersion = -1;
      this._latestUsedPaymentRatio = -1;
      this._cachedPartySizeRatio = 1f;
      this.VersionNo = 0;
      this._partyPureSpeedLastCheckVersion = -1;
      this._itemRosterVersionNo = -1;
      this.Party.InitCache();
    }

    protected override void AfterLoad()
    {
      this.Party.AfterLoad();
      if (this.IsGarrison && this.MapEvent == null && this.SiegeEvent == null && this.TargetParty != null && this.CurrentSettlement != null)
        this.Ai.SetMoveModeHold();
      if (this.CurrentSettlement != null && !this.CurrentSettlement.Parties.Contains(this))
      {
        this.CurrentSettlement.AddMobileParty(this);
        foreach (MobileParty attachedParty in (List<MobileParty>) this._attachedParties)
        {
          if (this.Army.LeaderParty != this)
            this.CurrentSettlement.AddMobileParty(attachedParty);
        }
      }
      if (this._doNotAttackMainParty > 0)
        this.Ai.DoNotAttackMainPartyUntil = CampaignTime.HoursFromNow((float) this._doNotAttackMainParty);
      if (this.IsCaravan && this.Army != null)
        this.Army = (Army) null;
      if (MBSaveLoad.IsUpdatingGameVersion && MBSaveLoad.LastLoadedGameVersion < ApplicationVersion.FromString("v1.1.0") && (this.PaymentLimit == 2000 || this == MobileParty.MainParty && this.PaymentLimit == 0))
        this.SetWagePaymentLimit(Campaign.Current.Models.PartyWageModel.MaxWage);
      if (MBSaveLoad.IsUpdatingGameVersion && MBSaveLoad.LastLoadedGameVersion < ApplicationVersion.FromString("v1.2.0") && this.IsCaravan && this.Owner == Hero.MainHero && this.ActualClan == null)
        this.ActualClan = this.Owner.Clan;
      if (!MBSaveLoad.IsUpdatingGameVersion || !(MBSaveLoad.LastLoadedGameVersion < ApplicationVersion.FromString("v1.1.4")))
        return;
      if (this.TargetParty != null)
      {
        IFaction mapFaction = this.TargetParty.MapFaction;
        if ((mapFaction != null ? (!mapFaction.IsAtWarWith(this.MapFaction) ? 1 : 0) : 1) != 0)
          goto label_28;
      }
      if (this.TargetSettlement != null)
      {
        IFaction mapFaction = this.TargetSettlement.MapFaction;
        if ((mapFaction != null ? (!mapFaction.IsAtWarWith(this.MapFaction) ? 1 : 0) : 1) != 0)
          goto label_28;
      }
      if (this.ShortTermTargetParty == null)
        return;
      MobileParty shortTermTargetParty = this.ShortTermTargetParty;
      int num;
      if (shortTermTargetParty == null)
      {
        num = 1;
      }
      else
      {
        bool? nullable = shortTermTargetParty.MapFaction?.IsAtWarWith(this.MapFaction);
        bool flag = true;
        num = !(nullable.GetValueOrDefault() == flag & nullable.HasValue) ? 1 : 0;
      }
      if (num == 0)
        return;
label_28:
      this.Ai.SetMoveModeHold();
    }

    internal void OnFinishLoadState() => Campaign.Current.MobilePartyLocator.UpdateLocator(this);

    int ILocatable<MobileParty>.LocatorNodeIndex
    {
      get => this._locatorNodeIndex;
      set => this._locatorNodeIndex = value;
    }

    [CachedData]
    public PathFaceRecord CurrentNavigationFace
    {
      get => this._currentNavigationFace;
      private set
      {
        this._lastNavigationFace = this.CurrentNavigationFace;
        this._currentNavigationFace = value;
      }
    }

    internal void HourlyTick()
    {
      if (!this.IsActive)
        return;
      if (this.LeaderHero != null && this.CurrentSettlement != null && this.CurrentSettlement == this.LeaderHero.HomeSettlement)
        ++this.LeaderHero.PassedTimeAtHomeSettlement;
      this.Ai.HourlyTick();
    }

    internal void DailyTick()
    {
      this.RecentEventsMorale -= this.RecentEventsMorale * 0.1f;
      if (this.LeaderHero == null)
        return;
      this.LeaderHero.PassedTimeAtHomeSettlement *= 0.9f;
    }

    [CachedData]
    public Vec2 ErrorPosition { get; private set; }

    internal void TickForStationaryMobileParty(
      ref MobileParty.CachedPartyVariables variables,
      float dt,
      float realDt)
    {
      if (this.StationaryStartTime == CampaignTime.Never)
        this.StationaryStartTime = CampaignTime.Now;
      this.CheckIsDisorganized();
      this.DoUpdatePosition(ref variables, dt, realDt);
      this.DoErrorCorrections(ref variables, realDt);
    }

    internal void TickForMovingMobileParty(
      ref MobileParty.CachedPartyVariables variables,
      float dt,
      float realDt)
    {
      this.ComputeNextMoveDistance(ref variables, dt);
      this.CommonMovingPartyTick(ref variables, dt, realDt);
    }

    internal void TickForMovingArmyLeader(
      ref MobileParty.CachedPartyVariables variables,
      float dt,
      float realDt)
    {
      this.ComputeNextMoveDistanceForArmyLeader(ref variables, dt);
      this.CommonMovingPartyTick(ref variables, dt, realDt);
    }

    internal void CommonMovingPartyTick(
      ref MobileParty.CachedPartyVariables variables,
      float dt,
      float realDt)
    {
      this.StationaryStartTime = CampaignTime.Never;
      this.CheckIsDisorganized();
      this.Ai.DoAiPathMode(ref variables);
      this.DoUpdatePosition(ref variables, dt, realDt);
      this.DoErrorCorrections(ref variables, realDt);
    }

    internal void InitializeCachedPartyVariables(ref MobileParty.CachedPartyVariables variables)
    {
      variables.HasMapEvent = this.MapEvent != null;
      variables.CurrentPosition = this.Position2D;
      variables.TargetPartyPositionAtFrameStart = Vec2.Invalid;
      variables.LastCurrentPosition = this.Position2D;
      variables.IsAttachedArmyMember = false;
      variables.IsMoving = this.IsMoving || this.IsMainParty;
      variables.IsArmyLeader = false;
      if (this.Army == null)
        return;
      if (this.Army.LeaderParty == this)
      {
        variables.IsArmyLeader = true;
      }
      else
      {
        if (!this.Army.LeaderParty.AttachedParties.Contains(this))
          return;
        variables.IsAttachedArmyMember = true;
        variables.IsMoving = this.IsMoving || this.Army.LeaderParty.IsMoving;
      }
    }

    internal void ComputeNextMoveDistanceForArmyLeader(
      ref MobileParty.CachedPartyVariables variables,
      float dt)
    {
      if ((double) dt > 0.0)
      {
        double speedForPartyUnified = (double) this.CalculateSpeedForPartyUnified();
        variables.NextMoveDistance = this.Speed * dt;
      }
      else
        variables.NextMoveDistance = 0.0f;
    }

    internal void ComputeNextMoveDistance(ref MobileParty.CachedPartyVariables variables, float dt)
    {
      if ((double) dt > 0.0)
      {
        double speed = (double) this.CalculateSpeed();
        variables.NextMoveDistance = this.Speed * dt;
      }
      else
        variables.NextMoveDistance = 0.0f;
    }

    internal void UpdateStationaryTimer()
    {
      if (!this.IsMoving)
      {
        if (!(this.StationaryStartTime == CampaignTime.Never))
          return;
        this.StationaryStartTime = CampaignTime.Now;
      }
      else
        this.StationaryStartTime = CampaignTime.Never;
    }

    private void CheckIsDisorganized()
    {
      if (!this._isDisorganized || !this._disorganizedUntilTime.IsPast)
        return;
      this.SetDisorganized(false);
    }

    public void SetDisorganized(bool isDisorganized)
    {
      if (isDisorganized)
        this._disorganizedUntilTime = CampaignTime.HoursFromNow(Campaign.Current.Models.PartyImpairmentModel.GetDisorganizedStateDuration(this));
      this._isDisorganized = isDisorganized;
      this.UpdateVersionNo();
    }

    internal void DoUpdatePosition(
      ref MobileParty.CachedPartyVariables variables,
      float dt,
      float realDt)
    {
      variables.NextPosition = variables.CurrentPosition;
      Vec2 vec2_1 = variables.CurrentPosition + this.EventPositionAdder + this.ArmyPositionAdder;
      Vec2 vec2_2;
      Vec2 vec2_3;
      if (variables.IsAttachedArmyMember)
      {
        if (variables.HasMapEvent || this.CurrentSettlement != null)
        {
          vec2_2 = Vec2.Zero;
        }
        else
        {
          Vec2 vec2_4 = variables.HasMapEvent ? this.Army.LeaderParty.Position2D : this.Army.LeaderParty.Ai.NextTargetPosition;
          Vec2 finalTargetPosition;
          this.Army.LeaderParty.Ai.GetTargetPositionAndFace(ref variables, out finalTargetPosition, out PathFaceRecord _, out bool _);
          Vec2 armyFacing = (double) (vec2_4 - this.Army.LeaderParty.Position2D).LengthSquared < 0.0025000001769512892 ? Vec2.FromRotation(this.Army.LeaderParty.Party.AverageBearingRotation) : (vec2_4 - this.Army.LeaderParty.Position2D).Normalized();
          Vec2 parentUnitF = armyFacing.TransformToParentUnitF(this.Army.GetRelativePositionForParty(this, armyFacing));
          vec2_2 = vec2_4 + parentUnitF - vec2_1;
          vec2_3 = finalTargetPosition + parentUnitF - vec2_1;
          if ((double) vec2_3.LengthSquared < 0.010000000707805157 || (double) vec2_2.LengthSquared < 0.010000000707805157)
            vec2_2 = Vec2.Zero;
          vec2_3 = vec2_2.LeftVec();
          vec2_3 = vec2_3.Normalized();
          float num = vec2_3.DotProduct(this.Army.LeaderParty.Position2D + parentUnitF - vec2_1);
          vec2_2.RotateCCW((double) num < 0.0 ? MathF.Max(num * 2f, -0.7853982f) : MathF.Min(num * 2f, 0.7853982f));
        }
      }
      else
        vec2_2 = (variables.HasMapEvent ? this.Party.MapEvent.Position : this.Ai.NextTargetPosition) - vec2_1;
      float num1 = vec2_2.Normalize();
      if ((double) num1 < (double) variables.NextMoveDistance)
        variables.NextMoveDistance = num1;
      if (this.BesiegedSettlement != null || this.CurrentSettlement != null || (double) variables.NextMoveDistance <= 0.0 && !variables.HasMapEvent)
        return;
      bool flag = false;
      Vec2 vec2_5 = this.Bearing;
      if ((double) num1 > 0.0)
      {
        flag = true;
        vec2_5 = vec2_2;
        if (!variables.IsAttachedArmyMember || !variables.HasMapEvent)
          this.Bearing = vec2_5;
      }
      else if (variables.IsAttachedArmyMember && variables.HasMapEvent)
      {
        vec2_5 = this.Army.LeaderParty.Bearing;
        this.Bearing = vec2_5;
        flag = true;
      }
      if (flag)
      {
        vec2_3 = this.Bearing;
        this.Party.AverageBearingRotation += MBMath.WrapAngle(vec2_3.RotationInRadians - this.Party.AverageBearingRotation) * MathF.Min((variables.HasMapEvent ? realDt : dt) * 30f, 1f);
        this.Party.AverageBearingRotation = MBMath.WrapAngle(this.Party.AverageBearingRotation);
      }
      variables.NextPosition = variables.CurrentPosition + vec2_5 * variables.NextMoveDistance;
    }

    internal void DoErrorCorrections(ref MobileParty.CachedPartyVariables variables, float realDt)
    {
      float lengthSquared = this.ErrorPosition.LengthSquared;
      if ((double) lengthSquared <= 0.0)
        return;
      if (this.CurrentSettlement != null || !this.IsVisible)
        this.ErrorPosition = Vec2.Zero;
      if ((double) lengthSquared <= 49.0 * (double) realDt * (double) realDt)
        this.ErrorPosition = Vec2.Zero;
      else
        this.ErrorPosition -= this.ErrorPosition.Normalized() * (7f * realDt);
    }

    internal void TickForMobileParty2(
      ref MobileParty.CachedPartyVariables variables,
      float realDt,
      ref int gridChangeCount,
      ref MobileParty[] gridChangeMobilePartyList)
    {
      variables.NextPathFaceRecord = Campaign.Current.MapSceneWrapper.GetFaceIndex(variables.NextPosition);
      if ((double) variables.NextMoveDistance <= 0.0 || !variables.IsMoving || this.BesiegedSettlement != null || variables.HasMapEvent)
        return;
      if (variables.IsAttachedArmyMember && (double) (this.Army.LeaderParty.Position2D - (this.Position2D + this.ArmyPositionAdder)).Length > 0.25)
      {
        this._position2D = this.Army.LeaderParty.Position2D;
        this.ArmyPositionAdder += variables.NextPosition - this.Position2D;
      }
      else
      {
        PathFaceRecord nextPathFaceRecord = variables.NextPathFaceRecord;
        if (!this.CurrentNavigationFace.IsValid() || this.CurrentNavigationFace.FaceIslandIndex != nextPathFaceRecord.FaceIslandIndex)
          return;
        this.SetPositionParallel(variables, variables.NextPosition, ref gridChangeCount, ref gridChangeMobilePartyList);
      }
    }

    public Vec2 EventPositionAdder
    {
      get => this._eventPositionAdder;
      set
      {
        this.ErrorPosition += this._eventPositionAdder;
        this._eventPositionAdder = value;
        this.ErrorPosition -= this._eventPositionAdder;
      }
    }

    public bool IsVisible
    {
      get => this._isVisible;
      set
      {
        if (this._isVisible == value)
          return;
        this._isVisible = value;
        this.Party.OnVisibilityChanged(value);
      }
    }

    public Vec2 Position2D
    {
      get => this._position2D;
      set
      {
        if (!(this._position2D != value))
          return;
        this._position2D = value;
        Campaign current = Campaign.Current;
        current.MobilePartyLocator.UpdateLocator(this);
        if (current.MapSceneWrapper == null)
          return;
        this.CurrentNavigationFace = current.MapSceneWrapper.GetFaceIndex(this._position2D);
      }
    }

    private void SetPositionParallel(
      MobileParty.CachedPartyVariables variables,
      Vec2 value,
      ref int GridChangeCounter,
      ref MobileParty[] GridChangeList)
    {
      if (!(this._position2D != value))
        return;
      this._position2D = value;
      if (!Campaign.Current.MobilePartyLocator.CheckWhetherPositionsAreInSameNode(value, (ILocatable<MobileParty>) this))
      {
        int index = Interlocked.Increment(ref GridChangeCounter);
        GridChangeList[index] = this;
      }
      this.CurrentNavigationFace = variables.NextPathFaceRecord;
    }

    public void SetCustomName(TextObject name) => this.CustomName = name;

    public void SetPartyUsedByQuest(bool isActivelyUsed)
    {
      if (this._isCurrentlyUsedByAQuest == isActivelyUsed)
        return;
      this._isCurrentlyUsedByAQuest = isActivelyUsed;
      CampaignEventDispatcher.Instance.OnMobilePartyQuestStatusChanged(this, isActivelyUsed);
    }

    public void ResetTargetParty() => this.TargetParty = (MobileParty) null;

    public bool IsInspected
    {
      get => this.Army != null && this.Army == MobileParty.MainParty.Army || this._isInspected;
      set => this._isInspected = value;
    }

    public void IgnoreForHours(float hours)
    {
      this._ignoredUntilTime = CampaignTime.HoursFromNow(hours);
    }

    public void IgnoreByOtherPartiesTill(CampaignTime time) => this._ignoredUntilTime = time;

    public void ResetBesiegedSettlement()
    {
      if (this._besiegerCampResetStarted)
        return;
      this._besiegerCampResetStarted = true;
      if (this.BesiegerCamp != null)
        this.BesiegerCamp = (BesiegerCamp) null;
      this._besiegerCampResetStarted = false;
    }

    public Vec2 GetPosition2D => this.Position2D;

    public int TotalWage
    {
      get => (int) Campaign.Current.Models.PartyWageModel.GetTotalWage(this).ResultNumber;
    }

    public ExplainedNumber TotalWageExplained
    {
      get => Campaign.Current.Models.PartyWageModel.GetTotalWage(this, true);
    }

    public MapEvent MapEvent => this.Party.MapEvent;

    internal void OnRemovedFromArmyInternal()
    {
      this.ResetTargetParty();
      if (!this.IsActive || this.LeaderHero == null)
        return;
      if (this.BesiegedSettlement != null && this.Army.LeaderParty != this)
      {
        if (this.BesiegedSettlement.SiegeEvent.BesiegerCamp.HasInvolvedPartyForEventType(this.Party, MapEvent.BattleTypes.Siege))
          return;
        if (this != MobileParty.MainParty)
        {
          if (this.MapEvent != null)
            return;
          this.Ai.SetMoveBesiegeSettlement(this.BesiegedSettlement);
        }
        else
          this.Ai.SetMoveModeHold();
      }
      else if (this.CurrentSettlement == null)
      {
        this.Ai.SetMoveModeHold();
      }
      else
      {
        if (this.Party.MapEvent != null)
          return;
        this.Ai.SetMoveGoToSettlement(this.CurrentSettlement);
      }
    }

    private void OnRemoveParty()
    {
      this.Army = (Army) null;
      this.CurrentSettlement = (Settlement) null;
      this.AttachedTo = (MobileParty) null;
      this.BesiegerCamp = (BesiegerCamp) null;
      List<Settlement> settlementList = new List<Settlement>();
      if (this.CurrentSettlement != null)
        settlementList.Add(this.CurrentSettlement);
      else if ((this.IsGarrison || this.IsMilitia || this.IsBandit || this.IsVillager) && this.HomeSettlement != null)
        settlementList.Add(this.HomeSettlement);
      this.PartyComponent?.Finish();
      this.ActualClan = (Clan) null;
      Campaign.Current.CampaignObjectManager.RemoveMobileParty(this);
      foreach (Settlement settlement in settlementList)
        settlement.SettlementComponent.OnRelatedPartyRemoved(this);
    }

    public void SetPartyObjective(MobileParty.PartyObjective objective)
    {
      this.Objective = objective;
    }

    public void UpdateVersionNo() => ++this.VersionNo;

    public TroopRoster MemberRoster => this.Party.MemberRoster;

    public TroopRoster PrisonRoster => this.Party.PrisonRoster;

    private bool IsLastSpeedCacheInvalid()
    {
      Hero leaderHero = this.LeaderHero;
      bool flag = !this.IsActive || leaderHero == null || leaderHero.PartyBelongedToAsPrisoner != null;
      bool isNight = Campaign.Current.IsNight;
      return this._lastNavigationFace.FaceIndex != this.CurrentNavigationFace.FaceIndex || this._partyLastCheckIsPrisoner != flag || this._partyLastCheckAtNight != isNight;
    }

    private bool IsBaseSpeedCacheInvalid()
    {
      MapWeatherModel.WeatherEventEffectOnTerrain terrainForPosition = Campaign.Current.Models.MapWeatherModel.GetWeatherEffectOnTerrainForPosition(this.Position2D);
      return this._partyPureSpeedLastCheckVersion != this.VersionNo || this._itemRosterVersionNo != this.Party.ItemRoster.VersionNo || this._lastWeatherTerrainEffect != terrainForPosition;
    }

    private float CalculateSpeedForPartyUnified()
    {
      bool flag1 = false;
      if (this.IsBaseSpeedCacheInvalid())
      {
        this._lastCalculatedBaseSpeedExplained = this.Army == null || !this.Army.LeaderParty.AttachedParties.Contains(this) ? Campaign.Current.Models.PartySpeedCalculatingModel.CalculateBaseSpeed(this) : this.Army.LeaderParty._lastCalculatedBaseSpeedExplained;
        MapWeatherModel.WeatherEventEffectOnTerrain terrainForPosition = Campaign.Current.Models.MapWeatherModel.GetWeatherEffectOnTerrainForPosition(this.Position2D);
        this._partyPureSpeedLastCheckVersion = this.VersionNo;
        this._itemRosterVersionNo = this.Party.ItemRoster.VersionNo;
        this._lastWeatherTerrainEffect = terrainForPosition;
        flag1 = true;
      }
      if (flag1)
        this._lastCalculatedSpeed = Campaign.Current.Models.PartySpeedCalculatingModel.CalculateFinalSpeed(this, this._lastCalculatedBaseSpeedExplained).ResultNumber;
      else if (this.IsLastSpeedCacheInvalid())
      {
        Hero leaderHero = this.LeaderHero;
        bool flag2 = !this.IsActive || leaderHero == null || leaderHero.PartyBelongedToAsPrisoner != null;
        bool isNight = Campaign.Current.IsNight;
        this._lastNavigationFace = this.CurrentNavigationFace;
        this._partyLastCheckIsPrisoner = flag2;
        this._partyLastCheckAtNight = isNight;
        this._lastCalculatedSpeed = Campaign.Current.Models.PartySpeedCalculatingModel.CalculateFinalSpeed(this, this._lastCalculatedBaseSpeedExplained).ResultNumber;
      }
      return this._lastCalculatedSpeed;
    }

    private float CalculateSpeed()
    {
      if (this.Army != null)
      {
        if (this.Army.LeaderParty.AttachedParties.Contains(this))
        {
          Vec2 armyFacing = ((this.Army.LeaderParty.MapEvent != null ? this.Army.LeaderParty.Position2D : this.Army.LeaderParty.Ai.NextTargetPosition) - this.Army.LeaderParty.Position2D).Normalized();
          return this.Army.LeaderParty._lastCalculatedSpeed * MBMath.ClampFloat((float) (1.0 + (double) this.Bearing.DotProduct(this.Army.LeaderParty.Position2D + armyFacing.TransformToParentUnitF(this.Army.GetRelativePositionForParty(this, armyFacing)) - this.VisualPosition2DWithoutError) * 1.0), 0.7f, 1.3f);
        }
      }
      else if (this.DefaultBehavior == AiBehavior.EscortParty && this.TargetParty != null && (double) this._lastCalculatedSpeed > (double) this.TargetParty._lastCalculatedSpeed)
        return this.TargetParty._lastCalculatedSpeed;
      return this.CalculateSpeedForPartyUnified();
    }

    public ItemRoster ItemRoster => this.Party.ItemRoster;

    public bool IsSpotted() => this.IsVisible;

    public bool IsMainParty => this == MobileParty.MainParty;

    public int AddElementToMemberRoster(
      CharacterObject element,
      int numberToAdd,
      bool insertAtFront = false)
    {
      return this.Party.AddElementToMemberRoster(element, numberToAdd, insertAtFront);
    }

    public int AddPrisoner(CharacterObject element, int numberToAdd)
    {
      return this.Party.AddPrisoner(element, numberToAdd);
    }

    public IFaction MapFaction
    {
      get
      {
        return this.ActualClan == null ? (this.Party.Owner == null ? (this.HomeSettlement == null ? (this.LeaderHero != null ? this.LeaderHero.MapFaction : (IFaction) null) : this.HomeSettlement.OwnerClan.MapFaction) : (this.Party.Owner != Hero.MainHero ? (!this.Party.Owner.IsNotable ? (!this.IsMilitia && !this.IsGarrison && !this.IsVillager || this.HomeSettlement?.OwnerClan == null ? (this.IsCaravan || this.IsBanditBossParty ? this.Party.Owner.MapFaction : (!this._isCurrentlyUsedByAQuest || this.Party.Owner == null ? (this.LeaderHero != null ? this.LeaderHero.MapFaction : (IFaction) null) : this.Party.Owner.MapFaction)) : this.HomeSettlement.OwnerClan.MapFaction) : this.Party.Owner.HomeSettlement.MapFaction) : this.Party.Owner.MapFaction)) : this.ActualClan.MapFaction;
      }
    }

    public TextObject ArmyName
    {
      get => this.Army == null || this.Army.LeaderParty != this ? this.Name : this.Army.Name;
    }

    public SiegeEvent SiegeEvent => this.BesiegerCamp?.SiegeEvent;

    public float Food
    {
      get => (float) this.Party.RemainingFoodPercentage * 0.01f + (float) this.TotalFoodAtInventory;
    }

    public Vec3 GetLogicalPosition()
    {
      float height = 0.0f;
      Campaign.Current.MapSceneWrapper.GetHeightAtPoint(this.Position2D, ref height);
      return new Vec3(this.Position2D.x, this.Position2D.y, height);
    }

    public int TotalFoodAtInventory => this.ItemRoster.TotalFood;

    public float TotalWeightCarried => this.ItemRoster.TotalWeight;

    public float SeeingRange
    {
      get => Campaign.Current.Models.MapVisibilityModel.GetPartySpottingRange(this).ResultNumber;
    }

    public Settlement BesiegedSettlement => this.BesiegerCamp?.SiegeEvent.BesiegedSettlement;

    public float GetTotalStrengthWithFollowers(bool includeNonAttachedArmyMembers = true)
    {
      MobileParty mobileParty = this.DefaultBehavior == AiBehavior.EscortParty ? this.TargetParty : this;
      float totalStrength = mobileParty.Party.TotalStrength;
      if (mobileParty.Army != null && mobileParty == mobileParty.Army.LeaderParty)
      {
        foreach (MobileParty party in (List<MobileParty>) mobileParty.Army.Parties)
        {
          if (party.Army.LeaderParty != party && party.AttachedTo != null | includeNonAttachedArmyMembers)
            totalStrength += party.Party.TotalStrength;
        }
      }
      return totalStrength;
    }

    private void FillPartyStacks(PartyTemplateObject pt, int troopNumberLimit = -1)
    {
      if (this.IsBandit)
      {
        float num1 = (float) (0.40000000596046448 + 0.800000011920929 * (double) Campaign.Current.PlayerProgress);
        int num2 = MBRandom.RandomInt(2);
        float num3 = num2 == 0 ? MBRandom.RandomFloat : (float) ((double) MBRandom.RandomFloat * (double) MBRandom.RandomFloat * (double) MBRandom.RandomFloat * 4.0);
        float num4 = num2 == 0 ? (float) ((double) num3 * 0.800000011920929 + 0.20000000298023224) : 1f + num3;
        float randomFloat1 = MBRandom.RandomFloat;
        float randomFloat2 = MBRandom.RandomFloat;
        float randomFloat3 = MBRandom.RandomFloat;
        float f1 = pt.Stacks.Count > 0 ? (float) pt.Stacks[0].MinValue + num1 * num4 * randomFloat1 * (float) (pt.Stacks[0].MaxValue - pt.Stacks[0].MinValue) : 0.0f;
        float f2 = pt.Stacks.Count > 1 ? (float) pt.Stacks[1].MinValue + num1 * num4 * randomFloat2 * (float) (pt.Stacks[1].MaxValue - pt.Stacks[1].MinValue) : 0.0f;
        float f3 = pt.Stacks.Count > 2 ? (float) pt.Stacks[2].MinValue + num1 * num4 * randomFloat3 * (float) (pt.Stacks[2].MaxValue - pt.Stacks[2].MinValue) : 0.0f;
        this.AddElementToMemberRoster(pt.Stacks[0].Character, MBRandom.RoundRandomized(f1));
        if (pt.Stacks.Count > 1)
          this.AddElementToMemberRoster(pt.Stacks[1].Character, MBRandom.RoundRandomized(f2));
        if (pt.Stacks.Count <= 2)
          return;
        this.AddElementToMemberRoster(pt.Stacks[2].Character, MBRandom.RoundRandomized(f3));
      }
      else if (troopNumberLimit < 0)
      {
        float playerProgress = Campaign.Current.PlayerProgress;
        for (int index = 0; index < pt.Stacks.Count; ++index)
        {
          int numberToAdd = (int) ((double) playerProgress * (double) (pt.Stacks[index].MaxValue - pt.Stacks[index].MinValue)) + pt.Stacks[index].MinValue;
          this.AddElementToMemberRoster(pt.Stacks[index].Character, numberToAdd);
        }
      }
      else
      {
        for (int index1 = 0; index1 < troopNumberLimit; ++index1)
        {
          int index2 = -1;
          float num5 = 0.0f;
          for (int index3 = 0; index3 < pt.Stacks.Count; ++index3)
            num5 += (float) ((!this.IsGarrison || !pt.Stacks[index3].Character.IsRanged ? (!this.IsGarrison || pt.Stacks[index3].Character.IsMounted ? 1.0 : 2.0) : 6.0) * ((double) (pt.Stacks[index3].MaxValue + pt.Stacks[index3].MinValue) / 2.0));
          float num6 = MBRandom.RandomFloat * num5;
          for (int index4 = 0; index4 < pt.Stacks.Count; ++index4)
          {
            num6 -= (float) ((!this.IsGarrison || !pt.Stacks[index4].Character.IsRanged ? (!this.IsGarrison || pt.Stacks[index4].Character.IsMounted ? 1.0 : 2.0) : 6.0) * ((double) (pt.Stacks[index4].MaxValue + pt.Stacks[index4].MinValue) / 2.0));
            if ((double) num6 < 0.0)
            {
              index2 = index4;
              break;
            }
          }
          if (index2 < 0)
            index2 = 0;
          this.AddElementToMemberRoster(pt.Stacks[index2].Character, 1);
        }
        int num = this.IsVillager ? 1 : 0;
      }
    }

    private void OnPartyJoinedSiegeInternal()
    {
      this._besiegerCamp.AddSiegePartyInternal(this);
      this._besiegerCamp.SetSiegeCampPartyPosition(this);
    }

    private void OnPartyLeftSiegeInternal()
    {
      this._besiegerCamp.RemoveSiegePartyInternal(this);
      this.EventPositionAdder = Vec2.Zero;
      this.ErrorPosition = Vec2.Zero;
    }

    public bool HasPerk(PerkObject perk, bool checkSecondaryRole = false)
    {
      switch (checkSecondaryRole ? (int) perk.SecondaryRole : (int) perk.PrimaryRole)
      {
        case 2:
          return this.LeaderHero != null && (this.LeaderHero.Clan?.Leader?.GetPerkValue(perk) ?? false);
        case 4:
          return this.Army?.LeaderParty?.LeaderHero?.GetPerkValue(perk) ?? false;
        case 5:
          Hero leaderHero1 = this.LeaderHero;
          return leaderHero1 != null && leaderHero1.GetPerkValue(perk);
        case 7:
          Hero effectiveSurgeon = this.EffectiveSurgeon;
          return effectiveSurgeon != null && effectiveSurgeon.GetPerkValue(perk);
        case 8:
          Hero effectiveEngineer = this.EffectiveEngineer;
          return effectiveEngineer != null && effectiveEngineer.GetPerkValue(perk);
        case 9:
          Hero effectiveScout = this.EffectiveScout;
          return effectiveScout != null && effectiveScout.GetPerkValue(perk);
        case 10:
          Hero effectiveQuartermaster = this.EffectiveQuartermaster;
          return effectiveQuartermaster != null && effectiveQuartermaster.GetPerkValue(perk);
        case 11:
          foreach (TroopRosterElement troopRosterElement in (List<TroopRosterElement>) this.MemberRoster.GetTroopRoster())
          {
            if (troopRosterElement.Character.IsHero && troopRosterElement.Character.HeroObject.GetPerkValue(perk))
              return true;
          }
          return false;
        case 12:
          Debug.FailedAssert("personal perk is called in party", "C:\\Develop\\MB3\\Source\\Bannerlord\\TaleWorlds.CampaignSystem\\Party\\MobileParty.cs", nameof (HasPerk), 1997);
          Hero leaderHero2 = this.LeaderHero;
          return leaderHero2 != null && leaderHero2.GetPerkValue(perk);
        default:
          return false;
      }
    }

    public bool IsEngaging
    {
      get
      {
        return this.DefaultBehavior == AiBehavior.EngageParty || this.ShortTermBehavior == AiBehavior.EngageParty;
      }
    }

    public SkillEffect.PerkRole GetHeroPerkRole(Hero hero)
    {
      if (this.Engineer == hero)
        return SkillEffect.PerkRole.Engineer;
      if (this.Quartermaster == hero)
        return SkillEffect.PerkRole.Quartermaster;
      if (this.Surgeon == hero)
        return SkillEffect.PerkRole.Surgeon;
      return this.Scout == hero ? SkillEffect.PerkRole.Scout : SkillEffect.PerkRole.None;
    }

    public void SetHeroPerkRole(Hero hero, SkillEffect.PerkRole perkRole)
    {
      switch (perkRole)
      {
        case SkillEffect.PerkRole.Surgeon:
          this.SetPartySurgeon(hero);
          break;
        case SkillEffect.PerkRole.Engineer:
          this.SetPartyEngineer(hero);
          break;
        case SkillEffect.PerkRole.Scout:
          this.SetPartyScout(hero);
          break;
        case SkillEffect.PerkRole.Quartermaster:
          this.SetPartyQuartermaster(hero);
          break;
      }
    }

    public void RemoveHeroPerkRole(Hero hero)
    {
      if (this.Engineer == hero)
        this.Engineer = (Hero) null;
      if (this.Quartermaster == hero)
        this.Quartermaster = (Hero) null;
      if (this.Surgeon == hero)
        this.Surgeon = (Hero) null;
      if (this.Scout == hero)
        this.Scout = (Hero) null;
      this.ResetCached();
    }

    public Hero GetRoleHolder(SkillEffect.PerkRole perkRole)
    {
      switch (perkRole)
      {
        case SkillEffect.PerkRole.PartyLeader:
          return this.LeaderHero;
        case SkillEffect.PerkRole.Surgeon:
          return this.Surgeon;
        case SkillEffect.PerkRole.Engineer:
          return this.Engineer;
        case SkillEffect.PerkRole.Scout:
          return this.Scout;
        case SkillEffect.PerkRole.Quartermaster:
          return this.Quartermaster;
        default:
          return (Hero) null;
      }
    }

    public Hero GetEffectiveRoleHolder(SkillEffect.PerkRole perkRole)
    {
      switch (perkRole)
      {
        case SkillEffect.PerkRole.PartyLeader:
          return this.LeaderHero;
        case SkillEffect.PerkRole.Surgeon:
          return this.EffectiveSurgeon;
        case SkillEffect.PerkRole.Engineer:
          return this.EffectiveEngineer;
        case SkillEffect.PerkRole.Scout:
          return this.EffectiveScout;
        case SkillEffect.PerkRole.Quartermaster:
          return this.EffectiveQuartermaster;
        default:
          return (Hero) null;
      }
    }

    internal bool IsCurrentlyEngagingSettlement
    {
      get
      {
        return this.ShortTermBehavior == AiBehavior.GoToSettlement || this.ShortTermBehavior == AiBehavior.RaidSettlement || this.ShortTermBehavior == AiBehavior.AssaultSettlement;
      }
    }

    public int GetNumDaysForFoodToLast()
    {
      int num = this.ItemRoster.TotalFood * 100;
      if (this == MobileParty.MainParty)
        num += this.Party.RemainingFoodPercentage;
      return (int) ((double) num / (100.0 * -(double) this.FoodChange));
    }

    internal bool IsCurrentlyEngagingParty => this.ShortTermBehavior == AiBehavior.EngageParty;

    public Vec3 GetPosition() => this.GetLogicalPosition();

    public bool IsCurrentlyGoingToSettlement => this.ShortTermBehavior == AiBehavior.GoToSettlement;

    public float GetTrackDistanceToMainAgent()
    {
      return this.GetPosition().Distance(Hero.MainHero.GetPosition());
    }

    public float PartySizeRatio
    {
      get
      {
        int versionNo = this.Party.MemberRoster.VersionNo;
        float cachedPartySizeRatio = this._cachedPartySizeRatio;
        if (this._partySizeRatioLastCheckVersion != versionNo || this == MobileParty.MainParty)
        {
          this._partySizeRatioLastCheckVersion = versionNo;
          this._cachedPartySizeRatio = (float) this.Party.NumberOfAllMembers / (float) this.Party.PartySizeLimit;
          cachedPartySizeRatio = this._cachedPartySizeRatio;
        }
        return cachedPartySizeRatio;
      }
    }

    public int LimitedPartySize
    {
      get
      {
        if (!this.HasLimitedWage())
          return this.Party.PartySizeLimit;
        int paymentLimit = this.Party.MobileParty.PaymentLimit;
        if (this._latestUsedPaymentRatio == paymentLimit && this != MobileParty.MainParty)
          return this._cachedPartySizeLimit;
        this._latestUsedPaymentRatio = paymentLimit;
        int limitedPartySize = MathF.Max(1, MathF.Min((int) ((double) (this.PaymentLimit - Math.Min(this.LeaderHero == null || this.Party.Owner == null || this.Party.Owner.Clan == null || this.LeaderHero == this.Party.Owner.Clan.Leader ? 0 : this.LeaderHero.CharacterObject.TroopWage, this.TotalWage)) / (double) Campaign.Current.AverageWage) + 1, this.Party.PartySizeLimit));
        this._cachedPartySizeLimit = limitedPartySize;
        return limitedPartySize;
      }
    }

    public bool CheckTracked(BasicCharacterObject basicCharacter)
    {
      return this.MemberRoster.GetTroopRoster().Any<TroopRosterElement>((Func<TroopRosterElement, bool>) (t => t.Character == basicCharacter)) || this.PrisonRoster.GetTroopRoster().Any<TroopRosterElement>((Func<TroopRosterElement, bool>) (t => t.Character == basicCharacter));
    }

    public Vec2 VisualPosition2DWithoutError
    {
      get => this.Position2D + this.EventPositionAdder + this.ArmyPositionAdder;
    }

    private Settlement DetermineRelatedBesiegedSettlementWhileDestroyingParty()
    {
      Settlement whileDestroyingParty = this.BesiegedSettlement ?? (this.ShortTermBehavior == AiBehavior.AssaultSettlement ? this.ShortTermTargetSettlement : (Settlement) null);
      if (whileDestroyingParty == null && (this.IsGarrison || this.IsMilitia) && this.CurrentSettlement != null)
      {
        MapEvent mapEvent = this.CurrentSettlement.LastAttackerParty?.MapEvent;
        if (mapEvent != null && (mapEvent.IsSiegeAssault || mapEvent.IsSiegeOutside || mapEvent.IsSallyOut) && mapEvent.DefeatedSide != BattleSideEnum.None && mapEvent.State == MapEventState.WaitingRemoval)
          whileDestroyingParty = this.CurrentSettlement;
      }
      return whileDestroyingParty;
    }

    public void RemoveParty()
    {
      this.IsActive = false;
      this.IsVisible = false;
      Settlement whileDestroyingParty = this.DetermineRelatedBesiegedSettlementWhileDestroyingParty();
      Campaign current = Campaign.Current;
      this.AttachedTo = (MobileParty) null;
      this.BesiegerCamp = (BesiegerCamp) null;
      this.ReleaseHeroPrisoners();
      this.ItemRoster.Clear();
      this.MemberRoster.Reset();
      this.PrisonRoster.Reset();
      Campaign.Current.MobilePartyLocator.RemoveLocatable(this);
      Campaign.Current.VisualTrackerManager.RemoveTrackedObject((ITrackableBase) this, true);
      CampaignEventDispatcher.Instance.OnPartyRemoved(this.Party);
      GC.SuppressFinalize((object) this.Party);
      foreach (MobileParty mobileParty in (List<MobileParty>) current.MobileParties)
      {
        bool flag = mobileParty.Ai.AiBehaviorPartyBase == this.Party || mobileParty.TargetSettlement != null && mobileParty.TargetSettlement == whileDestroyingParty && mobileParty.CurrentSettlement != whileDestroyingParty || mobileParty.ShortTermTargetSettlement != null && mobileParty.ShortTermTargetSettlement == whileDestroyingParty && mobileParty.CurrentSettlement != whileDestroyingParty;
        if (mobileParty.TargetParty != null && mobileParty.TargetParty == this)
        {
          mobileParty.ResetTargetParty();
          flag = true;
        }
        if (flag && mobileParty.TargetSettlement != null && (mobileParty.MapEvent == null || mobileParty.MapEvent.IsFinalized) && mobileParty.DefaultBehavior == AiBehavior.GoToSettlement)
        {
          Settlement targetSettlement = mobileParty.TargetSettlement;
          mobileParty.Ai.SetMoveModeHold();
          mobileParty.Ai.SetNavigationModeHold();
          mobileParty.Ai.SetMoveGoToSettlement(targetSettlement);
          flag = false;
        }
        if (flag)
        {
          mobileParty.Ai.SetMoveModeHold();
          mobileParty.Ai.SetNavigationModeHold();
        }
      }
      this.OnRemoveParty();
      this._customHomeSettlement = (Settlement) null;
    }

    public bool IsMoving
    {
      get
      {
        if (this.IsMainParty)
          return !Campaign.Current.IsMainPartyWaiting;
        if (this.Position2D != this.TargetPosition)
          return true;
        return this.MapEvent == null && this.BesiegedSettlement == null && this.CurrentSettlement == null && this.ShortTermBehavior != 0;
      }
    }

    private void ReleaseHeroPrisoners()
    {
      for (int index = this.PrisonRoster.Count - 1; index >= 0; --index)
      {
        if (this.PrisonRoster.GetElementNumber(index) > 0)
        {
          TroopRosterElement elementCopyAtIndex = this.PrisonRoster.GetElementCopyAtIndex(index);
          if (elementCopyAtIndex.Character.IsHero && !elementCopyAtIndex.Character.IsPlayerCharacter)
            EndCaptivityAction.ApplyByReleasedByChoice(elementCopyAtIndex.Character.HeroObject);
        }
      }
    }

    public bool ShouldBeIgnored => this._ignoredUntilTime.IsFuture;

    private void CreateFigure(Vec2 position, float spawnRadius)
    {
      Vec2 pointNearPosition = Campaign.Current.MapSceneWrapper.GetAccessiblePointNearPosition(position, spawnRadius);
      this.Position2D = new Vec2(pointNearPosition.x, pointNearPosition.y);
      Vec2 vec2 = new Vec2(1f, 0.0f);
      float angleInRadians = (float) ((double) MBRandom.RandomFloat * 2.0 * 3.1415927410125732);
      vec2.RotateCCW(angleInRadians);
      this.Bearing = vec2;
      this.Party.UpdateVisibilityAndInspected();
      this.StartUp();
    }

    internal void SendPartyToReachablePointAroundPosition(
      Vec2 centerPosition,
      float distanceLimit,
      float innerCenterMinimumDistanceLimit = 0.0f)
    {
      this.Ai.SetMoveGoToPoint(MobilePartyHelper.FindReachablePointAroundPosition(centerPosition, distanceLimit, innerCenterMinimumDistanceLimit));
    }

    internal void TeleportPartyToSafePosition(float maxRadius = 3.3f, float minRadius = 3f)
    {
      for (int index = 0; index < 15; ++index)
      {
        Vec2 pointAroundPosition = MobilePartyHelper.FindReachablePointAroundPosition(this.Position2D, maxRadius, minRadius);
        bool flag = true;
        LocatableSearchData<MobileParty> data = MobileParty.StartFindingLocatablesAroundPosition(pointAroundPosition, 1f);
        for (MobileParty nextLocatable = MobileParty.FindNextLocatable(ref data); nextLocatable != null; nextLocatable = MobileParty.FindNextLocatable(ref data))
        {
          if (nextLocatable.MapFaction.IsAtWarWith(this.MapFaction))
          {
            flag = false;
            break;
          }
        }
        if (flag)
        {
          this.Position2D = pointAroundPosition;
          break;
        }
      }
    }

    public float FoodChange
    {
      get
      {
        return Campaign.Current.Models.MobilePartyFoodConsumptionModel.CalculateDailyFoodConsumptionf(this, Campaign.Current.Models.MobilePartyFoodConsumptionModel.CalculateDailyBaseFoodConsumptionf(this)).ResultNumber;
      }
    }

    public float BaseFoodChange
    {
      get
      {
        return Campaign.Current.Models.MobilePartyFoodConsumptionModel.CalculateDailyBaseFoodConsumptionf(this).ResultNumber;
      }
    }

    public ExplainedNumber FoodChangeExplained
    {
      get
      {
        return Campaign.Current.Models.MobilePartyFoodConsumptionModel.CalculateDailyFoodConsumptionf(this, Campaign.Current.Models.MobilePartyFoodConsumptionModel.CalculateDailyBaseFoodConsumptionf(this, true));
      }
    }

    public float HealingRateForRegulars
    {
      get
      {
        return Campaign.Current.Models.PartyHealingModel.GetDailyHealingForRegulars(this).ResultNumber;
      }
    }

    public ExplainedNumber HealingRateForRegularsExplained
    {
      get => Campaign.Current.Models.PartyHealingModel.GetDailyHealingForRegulars(this, true);
    }

    public float HealingRateForHeroes
    {
      get
      {
        return Campaign.Current.Models.PartyHealingModel.GetDailyHealingHpForHeroes(this).ResultNumber;
      }
    }

    public ExplainedNumber HealingRateForHeroesExplained
    {
      get => Campaign.Current.Models.PartyHealingModel.GetDailyHealingHpForHeroes(this, true);
    }

    public ExplainedNumber SeeingRangeExplanation
    {
      get => Campaign.Current.Models.MapVisibilityModel.GetPartySpottingRange(this, true);
    }

    public int InventoryCapacity
    {
      get
      {
        return (int) Campaign.Current.Models.InventoryCapacityModel.CalculateInventoryCapacity(this).ResultNumber;
      }
    }

    public ExplainedNumber InventoryCapacityExplainedNumber
    {
      get => Campaign.Current.Models.InventoryCapacityModel.CalculateInventoryCapacity(this, true);
    }

    public MapEventSide MapEventSide
    {
      get => this.Party.MapEventSide;
      set => this.Party.MapEventSide = value;
    }

    public static LocatableSearchData<MobileParty> StartFindingLocatablesAroundPosition(
      Vec2 position,
      float radius)
    {
      return Campaign.Current.MobilePartyLocator.StartFindingLocatablesAroundPosition(position, radius);
    }

    public static MobileParty FindNextLocatable(ref LocatableSearchData<MobileParty> data)
    {
      return Campaign.Current.MobilePartyLocator.FindNextLocatable(ref data);
    }

    internal void OnHeroAdded(Hero hero) => hero.OnAddedToParty(this);

    internal void OnHeroRemoved(Hero hero) => hero.OnRemovedFromParty(this);

    internal void CheckExitingSettlementParallel(
      ref int exitingPartyCount,
      ref MobileParty[] exitingPartyList)
    {
      if (this.Ai.IsDisabled || this.ShortTermBehavior == AiBehavior.Hold || this.CurrentSettlement == null || this.IsCurrentlyGoingToSettlement && this.ShortTermTargetSettlement == this.CurrentSettlement || this.IsMainParty || this.Army != null && this.AttachedTo != null && this.Army.LeaderParty != this)
        return;
      int index = Interlocked.Increment(ref exitingPartyCount);
      exitingPartyList[index] = this;
    }

    public bool ComputeIsWaiting()
    {
      if ((double) (2f * this.Position2D - this.TargetPosition - this.Ai.NextTargetPosition).LengthSquared < 9.9999997473787516E-06 || this.DefaultBehavior == AiBehavior.Hold)
        return true;
      return (this.DefaultBehavior == AiBehavior.EngageParty || this.DefaultBehavior == AiBehavior.EscortParty) && this.Ai.AiBehaviorPartyBase != null && this.Ai.AiBehaviorPartyBase.IsValid && this.Ai.AiBehaviorPartyBase.IsActive && this.Ai.AiBehaviorPartyBase.IsMobile && this.Ai.AiBehaviorPartyBase.MobileParty.CurrentSettlement != null;
    }

    bool IMapEntity.ShowCircleAroundEntity => this.CurrentSettlement == null;

    void IMapEntity.OnOpenEncyclopedia()
    {
      if (!this.IsLordParty || this.LeaderHero == null)
        return;
      Campaign.Current.EncyclopediaManager.GoToLink(this.LeaderHero.EncyclopediaLink);
    }

    bool IMapEntity.OnMapClick(bool followModifierUsed)
    {
      if (this.IsMainParty)
        MobileParty.MainParty.Ai.SetMoveModeHold();
      else if (followModifierUsed)
        MobileParty.MainParty.Ai.SetMoveEscortParty(this);
      else
        MobileParty.MainParty.Ai.SetMoveEngageParty(this);
      return true;
    }

    void IMapEntity.OnHover()
    {
      if (this.Army?.LeaderParty == this)
        InformationManager.ShowTooltip(typeof (Army), (object) this.Army, (object) false, (object) true);
      else
        InformationManager.ShowTooltip(typeof (MobileParty), (object) this, (object) false, (object) true);
    }

    bool IMapEntity.IsEnemyOf(IFaction faction)
    {
      return FactionManager.IsAtWarAgainstFaction(this.MapFaction, faction);
    }

    bool IMapEntity.IsAllyOf(IFaction faction)
    {
      return FactionManager.IsAlliedWithFaction(this.MapFaction, faction);
    }

    public void GetMountAndHarnessVisualIdsForPartyIcon(
      out string mountStringId,
      out string harnessStringId)
    {
      mountStringId = "";
      harnessStringId = "";
      this._partyComponent?.GetMountAndHarnessVisualIdsForPartyIcon(this.Party, out mountStringId, out harnessStringId);
    }

    Vec2 IMapEntity.InteractionPosition => this.Position2D;

    bool IMapEntity.IsMobileEntity => true;

    void IMapEntity.OnPartyInteraction(MobileParty engagingParty)
    {
      MobileParty mobileParty = this;
      if (mobileParty.AttachedTo != null && engagingParty != mobileParty.AttachedTo)
        mobileParty = mobileParty.AttachedTo;
      bool flag = false;
      if (mobileParty.CurrentSettlement != null)
      {
        if (mobileParty.MapEvent != null)
          flag = mobileParty.MapEvent.MapEventSettlement == mobileParty.CurrentSettlement && (mobileParty.MapEvent.AttackerSide.LeaderParty.MapFaction == engagingParty.MapFaction || mobileParty.MapEvent.DefenderSide.LeaderParty.MapFaction == engagingParty.MapFaction);
      }
      else
        flag = engagingParty != MobileParty.MainParty || !mobileParty.IsEngaging || mobileParty.ShortTermTargetParty != MobileParty.MainParty;
      if (!flag)
        return;
      if (engagingParty == MobileParty.MainParty && Game.Current.GameStateManager.ActiveState is MapState activeState)
        activeState.OnMainPartyEncounter();
      EncounterManager.StartPartyEncounter(engagingParty.Party, mobileParty.Party);
    }

    public void InitializePartyTrade(int initialGold)
    {
      this.IsPartyTradeActive = true;
      this.PartyTradeGold = initialGold;
    }

    public void AddTaxGold(int amount) => this.PartyTradeTaxGold += amount;

    public static MobileParty CreateParty(
      string stringId,
      PartyComponent component,
      PartyComponent.OnPartyComponentCreatedDelegate delegateFunction = null)
    {
      stringId = Campaign.Current.CampaignObjectManager.FindNextUniqueStringId<MobileParty>(stringId);
      MobileParty party = new MobileParty();
      party.StringId = stringId;
      party._partyComponent = component;
      party.UpdatePartyComponentFlags();
      component?.SetMobilePartyInternal(party);
      if (delegateFunction != null)
        delegateFunction(party);
      party.PartyComponent?.Initialize(party);
      Campaign.Current.CampaignObjectManager.AddMobileParty(party);
      CampaignEventDispatcher.Instance.OnMobilePartyCreated(party);
      return party;
    }

    public CaravanPartyComponent CaravanPartyComponent
    {
      get => this._partyComponent as CaravanPartyComponent;
    }

    public WarPartyComponent WarPartyComponent => this._partyComponent as WarPartyComponent;

    public BanditPartyComponent BanditPartyComponent
    {
      get => this._partyComponent as BanditPartyComponent;
    }

    public LordPartyComponent LordPartyComponent => this._partyComponent as LordPartyComponent;

    public PartyComponent PartyComponent
    {
      get => this._partyComponent;
      set
      {
        if (this._partyComponent == value)
          return;
        if (this._partyComponent != null)
          this._partyComponent.Finish();
        Campaign.Current.CampaignObjectManager.BeforePartyComponentChanged(this);
        this._partyComponent = value;
        this.UpdatePartyComponentFlags();
        Campaign.Current.CampaignObjectManager.AfterPartyComponentChanged(this);
        if (this._partyComponent == null)
          return;
        this._partyComponent.Initialize(this);
      }
    }

    [CachedData]
    public bool IsMilitia { get; private set; }

    [CachedData]
    public bool IsLordParty { get; private set; }

    public void UpdatePartyComponentFlags()
    {
      this.IsLordParty = this._partyComponent is LordPartyComponent;
      this.IsVillager = this._partyComponent is VillagerPartyComponent;
      this.IsMilitia = this._partyComponent is MilitiaPartyComponent;
      this.IsCaravan = this._partyComponent is CaravanPartyComponent;
      this.IsGarrison = this._partyComponent is GarrisonPartyComponent;
      this.IsCustomParty = this._partyComponent is CustomPartyComponent;
      this.IsBandit = this._partyComponent is BanditPartyComponent;
    }

    [CachedData]
    public bool IsVillager { get; private set; }

    [CachedData]
    public bool IsCaravan { get; private set; }

    [CachedData]
    public bool IsGarrison { get; private set; }

    [CachedData]
    public bool IsCustomParty { get; private set; }

    [CachedData]
    public bool IsBandit { get; private set; }

    public bool IsBanditBossParty => this.IsBandit && this.BanditPartyComponent.IsBossParty;

    public bool AvoidHostileActions
    {
      get => this._partyComponent != null && this._partyComponent.AvoidHostileActions;
    }

    public enum PartyObjective
    {
      Neutral,
      Defensive,
      Aggressive,
      NumberOfPartyObjectives,
    }

    internal struct CachedPartyVariables
    {
      internal bool IsAttachedArmyMember;
      internal bool IsArmyLeader;
      internal bool IsMoving;
      internal bool HasMapEvent;
      internal float NextMoveDistance;
      internal Vec2 CurrentPosition;
      internal Vec2 LastCurrentPosition;
      internal Vec2 NextPosition;
      internal Vec2 TargetPartyPositionAtFrameStart;
      internal PathFaceRecord TargetPartyCurrentNavigationFaceAtFrameStart;
      internal PathFaceRecord NextPathFaceRecord;
    }
  }
}
